#include <assert.h>
#include <gc/gc.h>
#include <string.h>

#include "scope.h"

binding_t *get_binding(name_t *name, scope_t *first)
{
  binding_t *entry = first->first;
  
  while(entry)
  {
    if( entry->name == name_get_value(name) )
    //entry->name->head == name->head)//!u_strcasecmp(entry->name->head,name->head,0))
    {
      if(first->uses)
      {
        list_entry_t *use = first->uses->first;
        binding_t *found = 0;
        while(
          use
          /*Scope was used more recently than the present definition */
          && (((use_t*)use->head)->clock > entry->clock)
          /*Was a match found in the scope? */
          && !(found = get_binding(name,((use_t*)use->head)->scope))
          /*Find the next used scope */
          && (use = use->tail))
                { /*Do Nothing */ };
        if(found && !found->hidden)
        {
          return found;
        }
        else
        {
          return entry;
        }
      }
      return entry;
    }
    entry = entry->next;
  }
  
  if(first->uses)
  {
    list_entry_t *use = first->uses->first;
    binding_t *found = 0;
    while(
      use
      /*Was a match found in the scope? */
      && !(found = get_binding(name,((use_t*)use->head)->scope))
      /*Find the next used scope */
      && (use = use->tail))
            { /*Do Nothing */ };
    if(found && !found->hidden)
    {
      return found;
    }
  }
  
  return (binding_t*)0;
}

/*
 Should this return a pointer or a value? e.g.
  - bind(x,1) -> 1 @ 0xA
  - lookup(x) -> 0xA
  - bind(x,2) -> @ 0xA
  - *0xA = 2
  x may have propogated as a pointer and change value
*/

value_t *lookup_flat_symbol(scope_t *scope, name_t *name, handler_t *handler)
{

  scope_t *current_scope = scope;
  while(current_scope)
  {
    assert(has_type(current_scope, type_scope)); 
    binding_t *binding = get_binding(name,current_scope);
    if(binding)
    {
      return (value_t*)binding->value;
    }
    current_scope = current_scope->parent;
  }
  
  // Does not show the whole name - FIX
  const char *not_found = " not found";
  char *name_string = from_unicode(get_identifier_string(name_get_value(name))); //from_unicode(name->head);
  char *buffer = GC_MALLOC((strlen(not_found) + strlen(name_string) + 1) * sizeof(char));
  strcpy(buffer,name_string);
  strcat(buffer,not_found);
  exception_at(handler,(void*)0,"lookup", buffer);
  // Placate the compiler - we never reach here
  return (value_t*)0;
}

value_t *lookup(scope_t *scope, name_t *name, int nested, handler_t *handler)
{
  /*Slowgorithmz
   - this runs every time we look up a name
   - make it fast then. duh
    - hierarchical hash tables?
   - build "builtins" in some kind of frequency order?
   - rearrange the list dynamically based on frequency?
      - scopes are closure-wise, so may not work
   - cache names from parent upwards somehow?
   - scopes built from arguments have fixed initial size
     - use hash-table and offsets?
    - benchmark all of this against stdlib, etc
      - define standard benchmarks!
      - regression test against benchmarks
  */
  scope_t *current_scope = scope;
  while(current_scope)
  {
    assert(type_scope == current_scope->type);
    binding_t *binding = get_binding(name,current_scope);
    if(binding)
    {
      if(!name->parts)
      {
        //((value_t*)binding->value)->generic.data = name->data;
        return (value_t*)binding->value;
      }
      else if(!has_type(binding->value,type_scope))
      {
        exception_at(handler, (value_t*)name, "lookup",
          "Trying to look up name in something not a scope BETTER MESSAGE PLEASE");
      }
      current_scope = (scope_t*)binding->value;
      list_entry_t *part = name->parts->first;
      name_t *current_name = make_name(part->head);
      while(binding = (binding_t*)get_binding(current_name,current_scope))
      {
        if(binding && !binding->hidden)
        {
          if(!part->tail)
          {
            //((value_t*)binding->value)->generic.data = name->data;
            return (value_t*)binding->value;
          }
          else if(!has_type(binding->value, type_scope))
          {
            exception_at(handler, (value_t*)name, "lookup",
              "Trying to look up name in something not a scope BETTER MESSAGE PLEASE");
          }
          part = part->tail;
          current_name = make_name(part->head);
          current_scope = (scope_t*)binding->value;
          continue;
        }
        exception_at(handler,(value_t*)name,"lookup",
          "Name not found in [...]");
      }
    }
    current_scope = current_scope->parent;
  }
  
  // Does not show the whole name - FIX
  const char *not_found = " not found";
  char *name_string = from_unicode(get_identifier_string(name->head)); //from_unicode(name->head);
  char *buffer = GC_MALLOC((strlen(not_found) + strlen(name_string) + 1) * sizeof(char));
  strcpy(buffer,name_string);
  strcat(buffer,not_found);
  exception_at(handler,(value_t*)name,"lookup", buffer);
  // Placate the compiler - we never reach here
  return (value_t*)0;
}

// Rewrite to partition based on clocks?

void bind(scope_t *scope, name_t *name, value_t *value)
{
  binding_t *previous, *current = scope->first;
  previous = current;
  while(current)
  {
    if(current->name == name_get_value(name))//name_equal(current->name, name))
    {
      current->value = value;
      current->clock = scope->clock;
      return;
    }
    previous = current;
    current = current->next;
  }
  current = GC_MALLOC(sizeof(binding_t));
  current->clock = scope->clock;
  current->hidden = 0;
  if(previous)
    previous->next = current;
  else
    scope->first = current;
  current->name = name_get_value(name);
  current->value = value;
  current->next = 0;
  return;
}

/*
  This is getting hacky :(
*/

void use_scope(scope_t *user, scope_t *used)
{
  use_t *make_use = GC_MALLOC(sizeof(use_t));
  make_use->clock = ++(user->clock);
  make_use->scope = used;
  generic_list_insert(user->uses, make_use);
  return;
}

/*
  Creates a new scope with the given parent. 'names' is zipped with 'values',
  unless 'tail' is set, in which case #names values are zipped and those
  remaining are placed in a new list bound to tail.
*/

scope_t *make_scope(scope_t *parent, generic_list_t *name_list, generic_list_t *value_list, name_t *tail)
{
  scope_t *new_scope = (scope_t*)allocate_type(type_scope);
  new_scope->parent = parent;
  new_scope->uses = make_generic_list(NULL);
  new_scope->clock = 0;
  new_scope->first = 0; 
 
  list_entry_t *values = 0;
 
  if(value_list)
  {
    values = value_list->first;
  }

  if(name_list)
  {
    list_entry_t *names = name_list->first;
  
    while(names)
    {
      //assert(values);
      bind(new_scope, names->head, values->head);
      names = names->tail;
      values = values->tail;
    }
    
  }
 
  if(tail)
  {
    generic_list_t *remaining_values = splice_generic_list(values);
    list_t *remaining = make_list(remaining_values);
    bind(new_scope, tail, (value_t*)remaining);
    values = 0;
  }
 
  //assert(!values);
  
  return new_scope;
}

